package repair

import (
	"common/pb"
	rs2 "common/rs"
	"common/uuid"
	"context"
	"encoding/json"
	"euplink/center_rpc"
	"go.uber.org/zap"
	"time"
)

type Config struct {
	DoTaskInternal time.Duration `help:"取任务间隔" releaseDefault:"30s" default:"30s"`
	RpcAddress     string        `help:"地址" releaseDefault:"estore.guanjieai.com:10000" default:"estore.guanjieai.com:10000"`
}

type Service struct {
	log    *zap.Logger
	config *Config
	nodeId uuid.UUID
}

func NewService(log *zap.Logger, cfg Config, uid uuid.UUID) *Service {

	return &Service{
		log:    log,
		config: &cfg,
		nodeId: uid,
	}
}

func (s *Service) Run(ctx context.Context) error {
	getTaskTimer := time.NewTimer(time.Second * 3)
	for {
		select {
		case <-ctx.Done():
			break
		case <-getTaskTimer.C:
			t1 := time.Now()
			ok, err := s.GetTaskAndRepair(ctx)
			s.log.Info("GetTaskAndRepair result:", zap.Bool("result", ok), zap.Error(err))
			useTime := time.Now().Sub(t1)
			nextTaskTime := s.config.DoTaskInternal
			if ok {
				nextTaskTime = time.Second / 10
				if useTime < time.Second { //防止过快的请求
					nextTaskTime = time.Second
				}
			}
			if nextTaskTime < time.Second { //防止过快的请求
				nextTaskTime = time.Second
			}
			getTaskTimer.Reset(nextTaskTime)
		}
	}
}

// GetTaskAndRepair 返回 treu 表示取到了任务
func (s *Service) GetTaskAndRepair(ctx context.Context) (bool, error) {
	s.log.Debug("GetTaskAndRepair")
	client, connClose, err := getDrpcClient(ctx, s.config.RpcAddress, nil)
	if err != nil {
		return false, err
	}
	defer connClose()

	response, err := client.GetTask(ctx, &pb.TaskRequest{
		NodeId: s.nodeId.Bytes(),
	})
	if err != nil {
		return false, err
	}
	if response.Empty { //无可修复任务
		s.log.Info("response.Empty")
		return false, err
	}
	if len(response.StreamId) == 0 {
		s.log.Info("response.StreamId empty")
		return false, err
	}
	expireTime := time.Time{}
	if response.ExpireTime > 0 {
		expireTime = time.Unix(response.ExpireTime, 0)
	}
	//downloadNodes := ConvertNodeInfo(response.DownloadInfo)
	downloadNodes := center_rpc.ConverSegmentToNodeInfo(response.DownloadInfo)
	uploadNodes := ConvertNodeInfo(response.UploadInfo)
	var repairResultString = ""

	repairer := NewRepairer(s.log, response.StreamId, int(response.DownloadInfo.Index),
		int(response.DownloadInfo.Padding), expireTime, rs2.PbToRs(response.Rs),
		nil, downloadNodes, uploadNodes)
	result, err := repairer.Run(ctx)
	if err != nil {
		repairResultString = err.Error()
		//也要 commit
	}

	commit := &pb.CommitTaskRequest{
		StreamId:     response.StreamId,
		SegmentIndex: response.DownloadInfo.Index,
		RepairResult: []byte(repairResultString),
		Infos:        result,
	}
	_, err = client.CommitTask(ctx, commit)
	_commit, _ := json.Marshal(commit)
	s.log.Info("commit", zap.ByteString("commit", _commit), zap.Error(err))
	return true, err
}

func ConvertNodeInfo(in []*pb.NodeInfo) []center_rpc.NodeInfo {
	//转换数据
	nodeInfos := make([]center_rpc.NodeInfo, 0)
	for _, p := range in {
		_nodeId, _ := uuid.FromBytes(p.NodeId)
		nodeInfos = append(nodeInfos, center_rpc.NodeInfo{
			DevId:  p.StorageNodeAddress.DeviceId,
			Ip:     p.StorageNodeAddress.Ip,
			Ipv6:   p.StorageNodeAddress.Ipv6,
			Port:   uint16(p.StorageNodeAddress.Port),
			NodeId: _nodeId,
			//PieceNum: int(p.NodeId),
		})
	}
	//for i := range nodeInfos {
	//	fmt.Println(nodeInfos[i].String())
	//}
	return nodeInfos
}
